package gameoflife.gui;

import gameoflife.Cell;
import gameoflife.ChangeListener;
import gameoflife.Position;
import gameoflife.Rule;
import gameoflife.RunManager;
import gameoflife.automaton.Automaton;

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;

import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.SwingConstants;

@SuppressWarnings("serial")
public class StatusBar extends JPanel {

	private static final String RULES_FIELD_CARD = "FIELD_CARD";
	private static final String RULES_LABEL_CARD = "LABEL_CARD";

	private MouseListener rulePanelListener;

	private static StatusBar instance;

	static StatusBar getInstance() {
		return instance;
	}

	static StatusBar createInstance(RunManager runManager, Display display) {
		if (instance == null) {
			instance = new StatusBar(runManager, display);
		}
		return instance;
	}

	private StatusBar(final RunManager runManager, final Display display) {
		super();
		setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 0));

		final Automaton automaton = display.getAutomaton();

		// rules label
		final JPanel rulesPanel = new JPanel(new CardLayout());
		final JTextField textField = new JTextField();
		final JLabel rulesLabel = new JLabel() {
			@Override
			public String getText() {
				Rule rule = automaton.getRule();
				if (rule == null) {
					return "<rule not set>";
				}
				return rule.toString();
			}
		};
		rulesPanel.setToolTipText("Rules (click to edit)");
		rulesPanel.add(rulesLabel, RULES_LABEL_CARD);
		rulesPanel.add(textField, RULES_FIELD_CARD);
		rulePanelListener = new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				((CardLayout) rulesPanel.getLayout()).show(rulesPanel, RULES_FIELD_CARD);
				textField.setText(automaton.getRule().getRuleString());
				textField.selectAll();
				textField.requestFocus();
			}
		};
		rulesPanel.addMouseListener(rulePanelListener);

		textField.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Rule rule = Rule.createFromString(e.getActionCommand());
				if (rule != null) {
					automaton.setRule(rule);
				}
				textField.setText(null);
				((CardLayout) rulesPanel.getLayout()).show(rulesPanel, RULES_LABEL_CARD);
				StatusBar.this.getRootPane().getParent().requestFocus();
			}
		});

		// population label
		final JLabel populationLabel = new JLabel() {
			@Override
			public String getText() {
				return String.format("%d %3s %3s %3s",
						             automaton.size(), "+" + automaton.births(), "-" + automaton.deaths(),
						             automaton.generation());
			}
		};
		populationLabel.setToolTipText("Population, Growth, Decline, Generation");

		// scale label
		final JLabel scaleLabel = new JLabel() {
			@Override
			public String getText() {
				return display.getScaleString();
			};
		};
		scaleLabel.setToolTipText("Scale (cells:pixels)");

		// state label
		final JLabel stateLabel = new JLabel(){
			@Override
			public String getText() {
				RunManager.State state = runManager.getState();
				return state.toString() + " " +
				       runManager.getSpeed() + " " +
				       (state == RunManager.State.RUNNING ? (" " + runManager.getMovingAverage() + "ms") : "");
			}
		};
		stateLabel.setToolTipText("Automaton State, Speed & Computation Time");

		// position label
		final JLabel positionLabel = new JLabel(" ");
		positionLabel.setToolTipText("Position");

		add(positionLabel);
		addSeparator();
		add(populationLabel);
		addSeparator();
		add(rulesPanel);
		addSeparator();
		add(scaleLabel);
		addSeparator();
		add(stateLabel);

		// automaton changes
		automaton.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(int changeCode) {
				populationLabel.setText(populationLabel.getText());
				rulesLabel.setText(rulesLabel.getText());
			}
		});

		// run manager changes
		runManager.addChangeListener(new ChangeListener() {			
			@Override
			public void stateChanged(int changeCode) {
				stateLabel.setText(stateLabel.getText());
			}
		});

		// display changes
		display.setChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(int changeCode) {
				scaleLabel.setText(scaleLabel.getText());
			}
		});

		// display mouse position
		display.addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseDragged(MouseEvent e) {
				updatePosition(e);
			}
			@Override
			public void mouseMoved(MouseEvent e) {
				updatePosition(e);
			}
			private void updatePosition(MouseEvent e) {
				Position position = display.toPosition(e.getPoint());
				Cell cell = automaton.getCell(position);
				positionLabel.setText(cell == null ? position.toString() : cell.toString());
			}
		});
	}

	public void editRule() {
		rulePanelListener.mouseClicked(null);
	}

	private void addSeparator() {
		add(Box.createHorizontalStrut(5));
		JSeparator separator = new JSeparator(SwingConstants.VERTICAL);
		separator.setPreferredSize(new Dimension(2, getPreferredSize().height - 2));
		add(separator);
		add(Box.createHorizontalStrut(5));
	}
}
